#include "AudioFile.hpp"
#include <list>
#include <iostream>
#include <QtCore/QDataStream>
#include <QtCore/QByteArray>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
//#include <sys/mman.h>
#include <taglib/tag.h>
#include <taglib/audioproperties.h>
#include <taglib/fileref.h>
#include <taglib/flacfile.h>
#include <taglib/mpegfile.h>
#include <taglib/vorbisfile.h>
#include <taglib/id3v2tag.h>
#include <taglib/frames/attachedpictureframe.h>
#include <taglib/flacpicture.h>
#include <taglib/flacmetadatablock.h>
#include <taglib/xiphcomment.h>
#include <taglib/vorbisfile.h>
#include <taglib/tmap.h>
#include <taglib/mp4file.h>
#include <taglib/mp4tag.h>
#include <taglib/mp4coverart.h>

AudioFile::AudioFile(QString filepath) :tagEmpty(true), filename(filepath),trackNumber(0), trackCount(0),discNumber(0),discCount(0),time(0),year(0), decoder(NULL) {
    file=new QFile(filepath);
    fileinfo=new QFileInfo(filepath);
    type=fileinfo->suffix().toLower();
    filename=fileinfo->fileName();
    filedir=fileinfo->absoluteDir().absolutePath();
    readTag();
}
AudioFile::~AudioFile() {
    file->close();
    delete fileinfo;
    delete file;
}

/*QString AudioFile::getSampleFormat(sampleFormat sf) {
   switch(sf) {
       case int16Sample: return "16-bit PCM";   // i18n-hint: Audio data bit depth (precision): 16-bit integers
       case int24Sample: return "24-bit PCM";   // i18n-hint: Audio data bit depth (precision): 24-bit integers
       case floatSample: return "32-bit float"; // i18n-hint: Audio data bit depth (precision): 32-bit floating point
   }
   return "Unknown format"; //compiler food
}*/

void AudioFile::readTag() {
    TagLib::FileRef f(filename.toLocal8Bit().data(), true, TagLib::AudioProperties::Accurate);
    frequency=f.audioProperties()->sampleRate();
    bitrate=f.audioProperties()->bitrate();
    channelCount=f.audioProperties()->channels();
    time=f.audioProperties()->length();
    std::cout<<"File: "<<filename.toLocal8Bit().data()<<"\n\tSamplerate: "<<frequency
            <<"\n\tBitrate:  "<<bitrate
            <<"\n\tChannels: "<<channelCount
            <<"\n\tSamples:  "<<frequency*time
            <<"\n\tDuration: "<<time<<std::endl;
    //read tags
    if(!f.tag()->isEmpty()) {
        title=f.tag()->title().toCString();
        artist=f.tag()->artist().toCString();
        album=f.tag()->album().toCString();
        title=f.tag()->title().toCString();
        genre=f.tag()->genre().toCString();
        comment=f.tag()->comment().toCString();
        year=f.tag()->year();
        trackNumber=f.tag()->track();
        tagEmpty=false;
    }
    //get audio cover
    TagLib::ByteVector imageData;
    TagLib::String mime;
    bool hasPicture=false;
    if(type=="mp3") { //id3v2 tag
        TagLib::MPEG::File mp3File(filename.toLocal8Bit().data());
        TagLib::ID3v2::Tag *mp3tag=mp3File.ID3v2Tag();
        if(!mp3tag->isEmpty()) {
            TagLib::ID3v2::FrameList frames=mp3tag->frameList("APIC");
            if(frames.isEmpty()) return;
            TagLib::ID3v2::AttachedPictureFrame *frame=static_cast<TagLib::ID3v2::AttachedPictureFrame *>(frames.front());
            mime=frame->mimeType();
            imageData=frame->picture();
        }
    } else if(type=="flac") {
        TagLib::FLAC::File flac(filename.toLocal8Bit().data());
        TagLib::List<TagLib::FLAC::Picture*> flacPictures=flac.pictureList();
        for(std::list<TagLib::FLAC::Picture*>::const_iterator it=flacPictures.begin(); it!=flacPictures.end(); it++) {
            if((*it)->type()==TagLib::FLAC::Picture::FrontCover || (*it)->type()==TagLib::FLAC::Picture::Media) { //or (*it).type()==TagLib::FLAC::Picture::Illustration ?
                imageData=(*it)->data();
                mime=(*it)->mimeType();
                hasPicture=true;
                return;
            }
        }
    } else if(type=="ogg") { //http://wiki.xiph.org/VorbisComment#Cover_art
        TagLib::Ogg::Vorbis::File vorbis(filename.toLocal8Bit().data());
        TagLib::Ogg::XiphComment *comments=vorbis.tag();
        if(comments->contains("METADATA_BLOCK_PICTURE")) { //safer to check here than in the fieldListMap
            TagLib::String encodedB64=comments->fieldListMap()["METADATA_BLOCK_PICTURE"].front();
            TagLib::ByteVector data=QByteArray::fromBase64(encodedB64.toCString()).data();
            TagLib::FLAC::Picture *picture=new TagLib::FLAC::Picture(data);
            imageData=picture->data();
            mime=picture->mimeType();
            hasPicture=true;
            /*picture->setData(imageData);
            picture->setType((TagLib::FLAC::Picture::Type)  0x03); // FrontCover
            picture->setMimeType("image/jpeg");
            picture->setDescription("Front Cover");

            TagLib::ByteVector block = picture->render();
            tag->addField("METADATA_BLOCK_PICTURE", b64_encode(block.data(), block.size()), true);*/
        }
    } else if(type=="aac") {
        TagLib::MP4::File mp4(filename.toLocal8Bit().data());
        TagLib::MP4::Tag *mp4t=mp4.tag();
        TagLib::MP4::ItemListMap mp4ilm=mp4t->itemListMap();
        TagLib::MP4::CoverArt mp4ca = mp4ilm["covr"].toCoverArtList().front();
        imageData=mp4ca.data();
        switch(mp4ca.format()) {
            case TagLib::MP4::CoverArt::BMP: mime="image/bmp"; break;
            case TagLib::MP4::CoverArt::GIF: mime="image/gif"; break;
            case TagLib::MP4::CoverArt::JPEG: mime="image/jpeg"; break;
            case TagLib::MP4::CoverArt::PNG: mime="image/png"; break;
        }
        hasPicture=true;
    }
}

void AudioFile::load() {
    //todo
}

unsigned int AudioFile::getStartTime() {return silenceStartEnds;}
unsigned int AudioFile::getEndTime() {return silenceEndStarts;}
unsigned int AudioFile::getRate() {return frequency;}
QString AudioFile::getArtist() {return artist;}
QString AudioFile::getAlbum() {return album;}
QString AudioFile::getTitle() {return title;}
QString AudioFile::getGenre() {return genre;}
QString AudioFile::getComment() {return comment;}
QString AudioFile::getFilename() {return filename;}
QString AudioFile::getFiledir() {return filedir;}
QString AudioFile::getComposer() {return composer;}
unsigned int AudioFile::getTrackNumber() {return trackNumber;}
unsigned int AudioFile::getTrackCount() {return trackCount;}
unsigned int AudioFile::getDiscNumber() {return discNumber;}
unsigned int AudioFile::getDiscCount() {return discCount;}
unsigned int AudioFile::getYear() {return year;}
unsigned int AudioFile::getTime() {return time;}
unsigned int AudioFile::getFilesize() {return fileinfo->size();}
unsigned int AudioFile::getFrequency() {return frequency;}
unsigned int AudioFile::getSamplerate(){return frequency;}
unsigned int AudioFile::getChannels() {return channelCount;}
unsigned int AudioFile::getBitrate() {return bitrate;}
unsigned int AudioFile::getFrames() {return sampleCount;}
unsigned int AudioFile::getSamples(){return sampleCount;}
void AudioFile::setArtist(QString v){artist=v;}
void AudioFile::setAlbum(QString v) {album=v;}
void AudioFile::setTitle(QString v) {title=v;}
void AudioFile::setGenre(QString v) {genre=v;}
void AudioFile::setComment(QString v){genre=v;}
void AudioFile::setComposer(QString v){composer=v;}
void AudioFile::setTrackNumber(unsigned int v){trackNumber=v;}
void AudioFile::setTrackCount(unsigned int v) {trackCount=v;}
void AudioFile::setDiscNumber(unsigned int v) {discNumber=v;}
void AudioFile::setDiscCount(unsigned int v)  {discCount=v;}
void AudioFile::setYear(unsigned int v) {year=v;}

void AudioFile::prepareDecoding() {
    if(type=="mp3" || type=="mp2") {
        decoder=new MpgDecoder;
    } else if(type=="flac") {
        //decoder=new FlacDecoder;
    } else if(type=="ogg") {
        decoder=new VorbisDecoder;
    } else if(type=="wav") {
        decoder=new PcmDecoder;
    }
    decoder->load(filename.toLocal8Bit().data());
}
void AudioFile::endDecoding() {
    decoder->close();
    delete decoder;
    decoder=NULL;
}
Decoder* AudioFile::getDecoder() {
    return decoder;
}

void AudioFile::setId(unsigned int i) {id=i;}
unsigned int AudioFile::getId() {return id;}
bool AudioFile::isTagEmpty() {
    return tagEmpty;
}
